#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <array>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

using namespace std;

//进程之间客观会存在特定的协同工作场景
//一个进程要把自己的数据交付给另一个进程,让其进行处理
//那么这个就叫做进程间通信
//一个进程时看不到另一个进程的资源,因为进程具有独立性,交互数据,成本一定很高
//那么操作系统需要为其设计通信方式
//两个进程要互相通信,那么必须先看到同一份公共的资源
//这里的资源可能是以文件方式提供,也可能是队列的形式,还有可能是原始的内存块
//这就是通信方式很多种的原因
//进程间通信的前提本质:其实是由OS参与,提供一份所有通信进程能看见的公共资源

//进程间通信的目的
//1.数据传输:一个进程需要将它的数据发送到另一个进程
//2.资源共享:多个进程之间共享同样的资源
//3.通知事件:一个进程需要向另一个或一组进程发送消息
//通知他们发生了某种事件
//比如子进程终止时,通知父进程
//4.进程控制:有些进程希望完全控制另一个进程的执行(如debug)进程
//此时控制进程希望能够拦截另一个进程的所有陷入和异常
//并能够及时知道它的状态改变

//什么是管道?
//管道是unix中最古老的进程间通信的形式
//我们把从一个进程链接到另一个进程的一个数据流称为一个"管道"

//匿名管道
//#include<unistd.h>
//功能:创建匿名管道
//原型:
//int pipe(int fd[2])
//参数:
//fd:文件描述符数组,其中fd[0]表示读端,fd[1]表示写端
//返回值:
//创建成功会返回1,失败返回-1,并设置合适的错误码

//例子:从键盘读取数据,写入管道,读取管道,写到屏幕

//int main()
//{
//    int fd[2] = {0};
//
//    //创建匿名管道
//    if(pipe(fd) == -1)
//    {
//        cerr << "make pipe" << endl;
//        exit(1);
//    }
//
//    //read from stdin
//    char buffer[100];
//    int len;
//    while(fgets(buffer, 100, stdin))
//    {
//        len = strlen(buffer);
//        if (write(fd[1], buffer, len) != len)
//        {
//            cerr << "write pipe" << endl;
//            break;
//        }
//
//        memset(buffer, 0, 100);
//
//        //read from pipe 
//        if ((len = read(fd[0], buffer, 100)) == -1)
//        {
//            cerr << "read from pipe" << endl; 
//        }
//
//        //write to stdout
//        if (write(1, buffer, len) != len)
//        {
//            cerr << "write to stdout" << endl;
//            break;
//        }
//    }
//
//
//    return 0;
//}

//用fork来共享匿名管道
//情况1:读端不读或者读端读的慢,写端要等读端
//情况2:写端不写或者写端写的慢,读端要等写端
//int main()
//{
//    //创建管道
//    int pipe_fd[2] = {0};
//    if (pipe(pipe_fd) < 0)
//    {
//        cerr << "pipe create fail" << endl;
//        exit(1);
//    }
//
//    //创建子进程
//    pid_t id = fork();
//    if (id < 0)
//    {
//        //创建失败
//        cerr << "fork fail" << endl;
//        return 2;
//    }
//    else if(id == 0)
//    {
//        //子进程
//        //作为写段,关闭读端
//        close(pipe_fd[0]);
//
//        const char* msg = "i am child";
//
//        int count = 10;
//
//        while(count--)
//        {
//            //sleep(5);
//            ssize_t size_w  = write(pipe_fd[1], msg, strlen(msg));
//            if (size_w < 0)
//            {
//                cerr << "write fail" << endl;
//            }
//        }
//        
//    }
//    else 
//    {
//        //父进程
//        //作为读端,关闭写段
//        close(pipe_fd[1]);
//
//        char buffer[64];
//
//        while(1)
//        {
//            sleep(5); 
//            ssize_t size_r = read(pipe_fd[0], buffer, sizeof(buffer) - 1);
//            if (size_r > 0)
//            {
//                //设定结尾的标志
//                buffer[size_r] = '\0';
//                cout << "parent get messge from child:" << buffer << endl;
//            }
//            else if(size_r == 0)
//            {
//                cout << "parent process quit" << endl;
//                break;
//            }
//            else 
//            {
//                cerr << "read fail" << endl;
//                break;
//            }
//        }
//
//    }
//
//    return 0;
//}

//用fork来共享匿名管道
//情况3:读端关闭,写端收到SIGPIPE信号直接终止
//情况4:写端关闭,读端读完pipe内部的数据,然后就会读到0,表示读到文件结尾
//int main()
//{
//    //创建管道
//    int pipe_fd[2] = {0};
//    if (pipe(pipe_fd) < 0)
//    {
//        cerr << "pipe create fail" << endl;
//        exit(1);
//    }
//
//    //创建子进程
//    pid_t id = fork();
//    if (id < 0)
//    {
//        //创建失败
//        cerr << "fork fail" << endl;
//        return 2;
//    }
//    else if(id == 0)
//    {
//        //子进程
//        //作为写段,关闭读端
//        close(pipe_fd[0]);
//
//        const char* msg = "i am child";
//
//        int count = 10;
//
//        while(count--)
//        {
//            //sleep(1);
//            ssize_t size_w  = write(pipe_fd[1], msg, strlen(msg));
//
//            if (count == 1)
//            {
//                close(pipe_fd[1]);
//                break;
//            }
//
//            if (size_w < 0)
//            {
//                cerr << "write fail" << endl;
//            }
//        }
//        
//    }
//    else 
//    {
//        //父进程
//        //作为读端,关闭写段
//        close(pipe_fd[1]);
//
//        char buffer[64];
//
//        int count = 0;
//
//        while(1)
//        {
//            sleep(1); 
//            ssize_t size_r = read(pipe_fd[0], buffer, sizeof(buffer) - 1);
//            if (size_r > 0)
//            {
//                //设定结尾的标志
//                buffer[size_r] = '\0';
//                cout << "parent get messge from child:" << buffer << endl;
//                count++;
//                //if (count == 5)
//                //{
//                //    //关闭读端,写端收到SIGPIPE信号直接终止
//                //    //当我们读端关闭,写端还在写入,站在操作系统的角度是验证不合理的,已经没有读的了,但是你还在写入,那么就是浪费OS的资源
//                //    //OS会直接终止写入进程,向目标发送SIGPIPE信号
//                //    close(pipe_fd[0]);
//                //    break;
//                //}
//            }
//            else if(size_r == 0)
//            {
//                cout << "parent process quit" << endl;
//                break;
//            }
//            else 
//            {
//                cerr << "read fail" << endl;
//                break;
//            }
//        }
//
//    }
//
//    int status = 0;
//    if (waitpid(id, &status, 0) > 0)
//    {
//        cout << "child quit, wait success" << "exit code: " << ((status >> 8) & 0xFF) << " single :" << (status &0x7F ) << endl;
//    }
//
//    return 0;
//}

//用fork来共享匿名管道
//下面我们来验证一下管道的大小是多少?
//write写满64K的时候,writer就不在写入了,这个时候管道被塞满了
//这个时候要等读端读到4K的数据,那么写端才会继续进行数据的写入
//因为要保证数据的原子性
int main()
{
    //创建管道
    int pipe_fd[2] = {0};
    if (pipe(pipe_fd) < 0)
    {
        cerr << "pipe create fail" << endl;
        exit(1);
    }

    //创建子进程
    pid_t id = fork();
    if (id < 0)
    {
        //创建失败
        cerr << "fork fail" << endl;
        return 2;
    }
    else if(id == 0)
    {
        //子进程
        //作为写段,关闭读端
        close(pipe_fd[0]);

        int count = 0;
        char c = 'x';

        while(1)
        {

            ssize_t size_w  = write(pipe_fd[1], &c, 1);

            count++;

            if (size_w < 0)
            {
                cerr << "write fail" << endl;
            }

            cout << "write : " << count << endl;

        }
        
    }
    else 
    {
        //父进程
        //作为读端,关闭写段
        close(pipe_fd[1]);

        //为了保证数据的原子性写入,pipe_buf的大小至少是512bytes
        //在linux上pipe_buf的大小是4096bytes
        //当我们写入的数据不大于pipe_buf时,linux将保证数据写入的原子性
        //当我们写入的数据大于pipe_buf时,linux将不再保证数据写入的原子性
        char buffer[1024*4];

        while(1)
        {
            sleep(4);
            ssize_t size_r = read(pipe_fd[0], buffer, sizeof(buffer) - 1);
            if (size_r > 0)
            {
                //设定结尾的标志
                buffer[size_r] = '\0';
               //cout << "parent get messge from child:" << buffer << endl;
            }
            else if(size_r == 0)
            {
                cout << "parent process quit" << endl;
                break;
            }
            else 
            {
                cerr << "read fail" << endl;
                break;
            }
        }

    }

    return 0;
}

//下面我们总结一下匿名管道的一些规则和特点:
//匿名管道的读写规则:
//1.没有数据可读的时候
//O_NONBLOCK disable: read调用阻塞,即进程暂停执行,一直等到有数据来到为止
//O_NONBLOCK enable: read调用返回-1, errno值为EAGAIN
//2.当管道满的时候
//O_NONBLOCK disable：write调用阻塞，直到有进程读走4K数据
//O_NONBLOCK enable：调用返回-1，errno值为EAGAIN
//3.如果所有管道写端对应的文件描述符被关闭(引用计数),则read返回0
//4.如果所有管道读端对应的文件描述符被关闭(引用计数),则write操作会产生SIGPIPE信号,进而可能导致write进程退出
//5.当写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性
//6.当写入的数据量大于PIPE_BUF时,linux将不在保证写入的原子性
//如果把一个事务看做一个程序,它要么完整的被执行,要么完全不执行
//这种特性就叫做原子性

//匿名管道特点:
//1.只能用于共同祖先的进程(具有血缘关系的进程)之间进行通信
//一个管道由一个进程创建,然后改进程调用fork,子进程利用父进程的PCB
//生成了自己的PCB,其中的文件描述符也被继承下来了
//此后父子进程之间就可以应用该管道
//2.管道提供流式服务的(管道是面向字节流的)
//3.一般而言,进程退出,管道释放,所以管道的生命周期随进程
//4.一般而言,内核会对管道操作进行同步和互斥
//5.管道是半双工的(只能单向通信),数据只能向一个方向流动,如果需要双方互相进行通信,需要建立两个管道
